using System;
using System.Data;
using System.IO;
using System.Text;
using System.Web.Services;
using System.Web.Services.Protocols;
using CrmServiceWebReference;
using KalDev;
using Resources;

/// <summary>
/// ///
/// ///    Notes exceptions, exclusions, or any other funny's you need 
/// ///    to know about CombinedSearch
/// ///
/// Liquid Thought
/// Created by:Rihan
/// 
/// Creates a combined datatable
///     Fetch records from CRM and put them in a table
///     Fetch records from KMS and put them in a table
///  
///     Combine the table
/// 
///     
/// </summary>
[WebService(Namespace = "http://localhost/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CombinedSearch : WebService
{
    private static readonly object syncRoot = new object();
    private static volatile CombinedSearch instance;

    /// <summary>
    /// Search can contain 3 types of parameters,
    /// a persons name or
    /// MobilePhoneNumber or
    /// _email address
    /// If the string contains a space, we assume that it is a persons name
    /// if the string contains a @ sign,we assume  that it is a _email address
    /// for the rest we try and locate a phone number / cell phone number
    /// </summary>
    /// <param name="searchParameter"><list type="bullet">
    /// 		<item>Contact LastName</item>
    /// 		<item>MobilePhoneNumber</item>
    /// 		<item>EmailAddress</item>
    /// 	</list></param>
    /// <returns></returns>
    private DataTable searchResultsToReturn;

    /// <summary>
    /// Search method to use
    /// </summary>
    private SearchType SearchType;

    private Contact ShopperToSearchFor;

    private CombinedSearch()
    {
    }

    public static CombinedSearch Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new CombinedSearch();
                    }
                }
            }
            return instance;
        }
    }


    /// <summary>
    /// Sets the search type from search parameters.
    /// </summary>
    /// <param name="searchParameter">The search parameter.</param>
    private void SetSearchTypeFromSearchParameters(string searchParameter)
    {
        SearchType = SearchType.FullName;
        if (searchParameter.Contains("@"))
        {
            SearchType = SearchType.EmailAddress;
        }
        Int32 idNumber;
        if (searchParameter.Length == 13 || Int32.TryParse(searchParameter, out idNumber))
        {
            if (Int32.TryParse(searchParameter, out idNumber))
            {
                SearchType = SearchType.IdNumber;
            }
        }
        Int32 phoneNumber;
        if (Int32.TryParse(searchParameter, out phoneNumber))
        {
            SearchType = SearchType.PhoneNumber;
        }
    }

    [WebMethod]
    public DataTable Search(string searchParameter)
    {
        if (!searchParameter.Contains("xxxxxxxxxxxxxx xxxxxxxxxx"))
        {
            SetSearchTypeFromSearchParameters(searchParameter);

            convertSearchStringToFields(searchParameter);

            DataTable crmSearchResults = SearchCrm(ShopperToSearchFor);
            SearchResult KmsShoppers = SearchKms(ShopperToSearchFor);

            searchResultsToReturn = SearchResults();
            if (crmSearchResults.Rows.Count > 0 &&
                KmsShoppers.Shoppers != null
                )
            {
                foreach (Shopper shopper in KmsShoppers.Shoppers)
                {
                    bool ShopperExistInCrm = false;
                    foreach (DataRow row in crmSearchResults.Rows)
                    {
                        if (
                            shopper.Email.Contains(row[Resource.CrmContactEmailAddressFieldName].ToString())
                            )
                        {
                            ShopperExistInCrm = true;
                        }
                    }
                    //Adding a single row, if the data exisist in both systems
                    if (ShopperExistInCrm == false)
                    {
                        DataRow kmsShopperRow = searchResultsToReturn.NewRow();
                        kmsShopperRow["FirstName"] = shopper.FirstName;
                        kmsShopperRow["LastName"] = shopper.LastName;
                        kmsShopperRow["Email"] = shopper.Email;
                        kmsShopperRow["IdNumber"] = shopper.IdNumber;
                        kmsShopperRow["SystemIdentifier"] = shopper.ShopperId;

                        searchResultsToReturn.Rows.Add(kmsShopperRow);
                    }
                }
                //Adding the CRM Results
                foreach (DataRow crmResultRow in crmSearchResults.Rows)
                {
                    searchResultsToReturn.Rows.Add(CreateSearchResultsRow(crmResultRow));
                }
            }
            else
            {
                //Adding KMS Shoppers
                if (KmsShoppers.Shoppers != null)
                {
                    foreach (Shopper shopper in KmsShoppers.Shoppers)
                    {
                        DataRow kmsShopperRow = searchResultsToReturn.NewRow();
                        kmsShopperRow["FirstName"] = shopper.FirstName;
                        kmsShopperRow["LastName"] = shopper.LastName;
                        kmsShopperRow["Email"] = shopper.Email;
                        kmsShopperRow["IdNumber"] = shopper.IdNumber;
                        kmsShopperRow["SystemIdentifier"] = shopper.ShopperId;

                        searchResultsToReturn.Rows.Add(kmsShopperRow);
                    }
                }
                //Adding CrmContacts
                if (crmSearchResults.Rows.Count > 0)
                {
                    foreach (DataRow row in crmSearchResults.Rows)
                    {
                        searchResultsToReturn.Rows.Add(CreateSearchResultsRow(row));
                    }
                }
            }
        }
        return searchResultsToReturn;
    }

    /// <summary>
    /// Creates the search results row.
    /// Handles that some columsn may not have been return by CRM, and therefor null
    /// </summary>
    /// <param name="sourceSystemDataRow">The source system data row.</param>
    /// <returns></returns>
    private DataRow CreateSearchResultsRow(DataRow sourceSystemDataRow)
    {
        DataRow localRow = searchResultsToReturn.NewRow();

        try
        {
            localRow["FirstName"] = sourceSystemDataRow["firstname"];
        }
        catch
        {
            localRow["FirstName"] = "";
        }
        ;
        try
        {
            localRow["LastName"] = sourceSystemDataRow["lastname"];
        }
        catch
        {
            localRow["LastName"] = "";
        }
        try
        {
            localRow["Email"] = sourceSystemDataRow["emailaddress1"];
        }
        catch
        {
            localRow["Email"] = "";
        }
        try
        {
            localRow["IdNumber"] = sourceSystemDataRow["new_idnumber"];
        }
        catch
        {
            localRow["IdNumber"] = "";
        }
        localRow["SystemIdentifier"] = sourceSystemDataRow["contactid"];

        return localRow;
    }

    /// <summary>
    /// Searches the results.
    /// </summary>
    /// <returns></returns>
    private static DataTable SearchResults()
    {
        DataTable localTable = new DataTable();
        localTable.TableName = "searchResultsToReturn";
        localTable.Columns.Add("FirstName");
        localTable.Columns.Add("LastName");
        localTable.Columns.Add("Email");
        localTable.Columns.Add("IdNumber");
        localTable.Columns.Add("SystemIdentifier");
        return localTable;
    }


    /// <summary>
    /// Converts the search string to fields.
    /// This needs to be called before any of the search method's are executed, so that the local fields
    /// can be populated
    /// using the <typeparamref name="searchType"/> TypeOfSearchToPeform type
    /// <list type="bullet">
    /// <item>If it is a _email address, assign's the searchParameterToConvert to EmailAddress</item>
    /// <item>If it contains a space, splits the string using char.Parse(" ") and assign's the first value to first name, and
    /// second value to <paramref name="LastName"/>LastName</item>
    /// <item>If it is none of the above, assign's it to phone number</item>
    /// </list>
    /// When it is a full name search, it takes the string and tries to split it on space's
    /// if there is more than one spaces, the first "Word" will go to first name and all the subsequent words will go to last name.
    /// </summary>
    /// <param name="searchParameterToConvert">The search parameter to convert.</param>
    private void convertSearchStringToFields(string searchParameterToConvert)
    {
        ShopperToSearchFor = new Contact();

        switch (SearchType)
        {
            case SearchType.EmailAddress:
                ShopperToSearchFor.EmailAddress = searchParameterToConvert;
                break;

            case SearchType.FullName:
                string[] NamedSearchParameters = searchParameterToConvert.Split(char.Parse(" "));

                if (NamedSearchParameters.Length > 2)
                {
                    ShopperToSearchFor.FirstName = NamedSearchParameters.GetValue(0).ToString();

                    for (int i = 1; NamedSearchParameters.Length > i; ++i)
                    {
                        ShopperToSearchFor.LastName = ShopperToSearchFor.LastName + " " +
                                                      NamedSearchParameters.GetValue(i).ToString();
                    }
                }
                else
                {
                    ShopperToSearchFor.FirstName = NamedSearchParameters.GetValue(0).ToString();
                    if (NamedSearchParameters.Length > 1)
                    {
                        ShopperToSearchFor.LastName = NamedSearchParameters.GetValue(1).ToString();
                    }
                }
                break;

            case SearchType.PhoneNumber:
                ShopperToSearchFor.MobileNumber = searchParameterToConvert;
                ShopperToSearchFor.PhoneNumber = searchParameterToConvert;
                break;

            case SearchType.ShopperId:
                ShopperToSearchFor.ShopperId = searchParameterToConvert;
                break;
        }
    }

    /// <summary>
    /// Fetches the parameters.
    /// </summary>
    /// <param name="ContactToCreateFetchParametersFrom">The contact to create fetch parameters from.</param>
    /// <returns></returns>
    private string FetchParameters(Contact ContactToCreateFetchParametersFrom)
    {
        StringBuilder fetchParameters = new StringBuilder();

        switch (SearchType)
        {
            case SearchType.FullName:
                fetchParameters.AppendFormat(Resource.FullNameSearchParameter,
                                             ContactToCreateFetchParametersFrom.FirstName,
                                             ContactToCreateFetchParametersFrom.LastName);
                break;
            case SearchType.EmailAddress:
                fetchParameters.AppendFormat(Resource.EmailSearchParameter,
                                             ContactToCreateFetchParametersFrom.EmailAddress);
                break;
            case SearchType.IdNumber:
                fetchParameters.AppendFormat(Resource.IdNumberSearchParameter,
                                             ContactToCreateFetchParametersFrom.PersonIdNumber);
                break;
            case SearchType.PhoneNumber:
                fetchParameters.AppendFormat(Resource.TelephoneNumberSearchParameter,
                                             ContactToCreateFetchParametersFrom.PhoneNumber);
                break;
        }

        return fetchParameters.ToString();
    }

    /// <summary>
    /// Searches crm,using fetch statements that is included in the resources file
    /// </summary>
    /// <param name="ContactToSearchFor">The contact to search for</param>
    /// <returns>Returns null, if no records are found</returns>
    private DataTable SearchCrm(Contact ContactToSearchFor)
    {
        StringBuilder xmlFetch = new StringBuilder();
        xmlFetch.AppendFormat(Resource.XmlFetchSearchContacts, FetchParameters(ContactToSearchFor));

        // Fetch the results.
        String results;
        try
        {
            CrmService localService = Common.CrmAutorize();
            results = localService.Fetch(xmlFetch.ToString());
        }

        catch (SoapException soapException)
        {
            results = soapException.Detail.InnerXml;
            Common.LogErrorMessage(soapException);
        }
        catch (Exception error)
        {
            results = error.Message;
            Common.LogErrorMessage(error);
        }
        if (results != Resource.NoResults)
        {
            return ConvertIntoDataTable(results);
        }
        else
        {
            DataTable emptyTable = new DataTable();
            return emptyTable;
        }
    }

    /// <summary>
    /// Converts the crm results tring into tabledata .
    /// </summary>
    /// <param name="CrmFetchResultsString">The CRM fetch results string.</param>
    /// <returns>the result table</returns>
    private static DataTable ConvertIntoDataTable(string CrmFetchResultsString)
    {
        DataSet ds = new DataSet();
        DataTable dataTable = new DataTable();
        StringReader reader = new StringReader(CrmFetchResultsString);
        ds.ReadXml(reader);
        return ds.Tables["result"];
    }

    /// <summary>
    /// Searches the KMS.
    /// by calling the SearchShoppers web services
    /// then creating the authorization header, using username and password that is
    /// specified in the resources file
    /// </summary>
    /// <returns>No restrictions on the amount of shoppers that it searches on</returns>
    private static SearchResult SearchKms(Contact shopperToSearchFor)
    {
        CRMService kmsSearchService = Common.KmsAuthorize();

        SearchResult result = kmsSearchService.SearchShoppers
            (shopperToSearchFor.FirstName
             , shopperToSearchFor.LastName
             , shopperToSearchFor.PhoneNumber
             , shopperToSearchFor.MobileNumber
             , shopperToSearchFor.EmailAddress
             , shopperToSearchFor.ShopperId
             , shopperToSearchFor.OrderNumber
            );
        return result;
    }
}